﻿#include "audioFilter.h"

AudioFilter::AudioFilter()
    : m_initialized(false), m_objFilterGraph(nullptr)
{
    m_pAudioSinkInfo = std::make_shared<AudioInfo>();
    m_pAudioSinkInfo->name = "sink";

    av_log_set_level(AV_LOG_TRACE);
}

AudioFilter::~AudioFilter()
{
    exit();
}

int AudioFilter::addAudioInput(uint32_t index, uint32_t samplerate, uint32_t channels, uint32_t samplingAccuracy, AVSampleFormat format)
{
    std::lock_guard<std::mutex> locker(m_mutex);

    if (m_initialized)
        return -1;

    if (m_mapAudioInputInfo.find(index) != m_mapAudioInputInfo.end())
        return -1;

    auto &info = m_mapAudioInputInfo[index];
    info.samplerate = samplerate;
    info.channels = channels;
    info.samplingAccuracy = samplingAccuracy;
    info.format = format;
    info.name = std::string("input") + std::to_string(index);
    info.filterCtx = nullptr;
    return 0;
}

int AudioFilter::addAudioOutput(const uint32_t samplerate, const uint32_t channels,
                                const uint32_t samplingAccuracy, const AVSampleFormat format)
{
    std::lock_guard<std::mutex> locker(m_mutex);

    if (m_initialized)
        return -1;

    m_pAudioOutputInfo = std::make_shared<AudioInfo>();
    m_pAudioOutputInfo->samplerate = samplerate;
    m_pAudioOutputInfo->channels = channels;
    m_pAudioOutputInfo->samplingAccuracy = samplingAccuracy;
    m_pAudioOutputInfo->format = format;
    m_pAudioOutputInfo->name = "output";
    m_pAudioOutputInfo->filterCtx = nullptr;
    return 0;
}

int AudioFilter::init(const char *duration)
{
    std::lock_guard<std::mutex> locker(m_mutex);
    if (m_initialized)
        return 0;

    if (m_mapAudioInputInfo.size() != 1)
        return -1;

    int ret;
    char args[512] = {0};

    m_objFilterGraph = avfilter_graph_alloc();
    if (!m_objFilterGraph)
    {
        printf("avfilter_graph_alloc error");
        return -1;
    }
    auto &objAudioInputInfo = m_mapAudioInputInfo.begin()->second;
    const AVFilter *abuffersrc = avfilter_get_by_name("abuffer");
    snprintf(args, sizeof(args),
             "sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
             objAudioInputInfo.samplerate,
             av_get_sample_fmt_name(objAudioInputInfo.format),
             av_get_default_channel_layout(objAudioInputInfo.channels));

    printf("input(%d) args: %s\n", m_mapAudioInputInfo.begin()->first, args);
    ret = avfilter_graph_create_filter(&m_pAudioSinkInfo->filterCtx, abuffersrc, "in",
                                       args, NULL, m_objFilterGraph);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
        return -1;
    }

    const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
    ret = avfilter_graph_create_filter(&m_pAudioSinkInfo->filterCtx, abuffersink, "out",
                                       NULL, NULL, m_objFilterGraph);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");
        return -1;
    }

    /* buffer audio sink: to terminate the filter chain. */

    const enum AVSampleFormat out_sample_fmts[] = {AV_SAMPLE_FMT_S16, -1};
    const int64_t out_channel_layouts[] = {AV_CH_LAYOUT_MONO, -1};
    const int out_sample_rates[] = {8000, -1};
    ret = avfilter_graph_create_filter(&m_pAudioSinkInfo->filterCtx, abuffersink, "out",
                                       NULL, NULL, m_objFilterGraph);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");
        return -1;
    }

    ret = av_opt_set_int_list(m_pAudioSinkInfo->filterCtx, "sample_fmts", out_sample_fmts, -1,
                              AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n");
        return -1;
    }

    ret = av_opt_set_int_list(m_pAudioSinkInfo->filterCtx, "channel_layouts", out_channel_layouts, -1,
                              AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n");
        return -1;
    }

    ret = av_opt_set_int_list(m_pAudioSinkInfo->filterCtx, "sample_rates", out_sample_rates, -1,
                              AV_OPT_SEARCH_CHILDREN);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");
        return -1;
    }

    AVFilterInOut *inputs = avfilter_inout_alloc();
    AVFilterInOut *outputs = avfilter_inout_alloc();
    inputs->name = av_strdup("in");
    inputs->filter_ctx = objAudioInputInfo.filterCtx;
    inputs->pad_idx = 0;
    inputs->next = nullptr;

    outputs->name = av_strdup("out");
    outputs->filter_ctx = m_pAudioOutputInfo->filterCtx;
    outputs->pad_idx = 0;
    outputs->next = nullptr;

    printf("m_strCustomFilterDescr=%s \n", m_strCustomFilterDescr.c_str());

    if ((ret = avfilter_graph_parse_ptr(m_objFilterGraph, m_strCustomFilterDescr.c_str(),
                                        &inputs, &outputs, NULL)) < 0)
    {
        printf("avfilter_graph_parse_ptr err");
        avfilter_inout_free(&inputs);
        avfilter_inout_free(&outputs);
        return -1;
    }

    if ((ret = avfilter_graph_config(m_objFilterGraph, NULL)) < 0)
    {
        printf("avfilter_graph_config err");
        avfilter_inout_free(&inputs);
        avfilter_inout_free(&outputs);
        return -1;
    }

    // todo 输出控制
    m_initialized = true;
    return 0;

    // // ======================================
    // // abuffersink
    // const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
    // m_pAudioSinkInfo->filterCtx = avfilter_graph_alloc_filter(m_objFilterGraph, abuffersink, "sink");
    // if (avfilter_init_str(m_pAudioSinkInfo->filterCtx, nullptr) != 0)
    // {
    //     printf("avfilter_init_str(abuffersink) failed.\n");
    //     return -1;
    // }
    // // abuffer输入
    // auto &objAudioInputInfo = m_mapAudioInputInfo.begin()->second;
    // const AVFilter *abuffer = avfilter_get_by_name("abuffer");

    // ret = avfilter_graph_create_filter(&buffersrc_ctx, abuffersrc, "in",
    //                                    args, NULL, m_objFilterGraph);
    // if (ret < 0)
    // {
    //     av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
    //     goto end;
    // }

    // snprintf(args, sizeof(args),
    //          "sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
    //          objAudioInputInfo.samplerate,
    //          av_get_sample_fmt_name(objAudioInputInfo.format),
    //          av_get_default_channel_layout(objAudioInputInfo.channels));

    // printf("input(%d) args: %s\n", m_mapAudioInputInfo.begin()->first, args);
    // objAudioInputInfo.filterCtx = avfilter_graph_alloc_filter(m_objFilterGraph, abuffer, objAudioInputInfo.name.c_str());
    // if (avfilter_init_str(objAudioInputInfo.filterCtx, args) != 0)
    // {
    //     printf("avfilter_init_str(abuffer) failed.\n");
    //     return -1;
    // }

    // AVFilterContext *last_ctx = objAudioInputInfo.filterCtx;
    // printf("m_strCustomFilterDescr=%s \n", m_strCustomFilterDescr.c_str());
    // // 可选自定义滤镜链（插入 abuffer 和 sink 之间）
    // if (!m_strCustomFilterDescr.empty())
    // {
    //     AVFilterInOut *inputs = avfilter_inout_alloc();
    //     AVFilterInOut *outputs = avfilter_inout_alloc();
    //     if (!inputs || !outputs)
    //     {
    //         printf("avfilter_inout_alloc avfilter_inout_alloc error");
    //         return -1;
    //     }

    //     inputs->name = av_strdup("in");
    //     inputs->filter_ctx = objAudioInputInfo.filterCtx;
    //     inputs->pad_idx = 0;
    //     inputs->next = nullptr;

    //     outputs->name = av_strdup("out");
    //     outputs->filter_ctx = m_pAudioOutputInfo->filterCtx;
    //     outputs->pad_idx = 0;
    //     outputs->next = nullptr;

    //     if (avfilter_graph_parse_ptr(m_objFilterGraph, m_strCustomFilterDescr.c_str(), &inputs, &outputs, nullptr) < 0)
    //     {
    //         printf("avfilter_graph_parse_ptr error");
    //         avfilter_inout_free(&inputs);
    //         avfilter_inout_free(&outputs);
    //         return -1;
    //     }
    //     avfilter_inout_free(&inputs);
    //     avfilter_inout_free(&outputs);

    //     // parse_ptr 已自动连接 abuffer->自定义链->sink，无需再手动 link
    //     last_ctx = nullptr; // 不再需要
    // }

    // // 输出格式转换（可选）
    // if (m_pAudioOutputInfo)
    // {
    //     const AVFilter *aformat = avfilter_get_by_name("aformat");
    //     snprintf(args, sizeof(args),
    //              "sample_rates=%d:sample_fmts=%s:channel_layouts=0x%" PRIx64,
    //              m_pAudioOutputInfo->samplerate,
    //              av_get_sample_fmt_name(m_pAudioOutputInfo->format),
    //              av_get_default_channel_layout(m_pAudioOutputInfo->channels));
    //     m_pAudioOutputInfo->filterCtx = avfilter_graph_alloc_filter(m_objFilterGraph, aformat, "aformat");
    //     if (avfilter_init_str(m_pAudioOutputInfo->filterCtx, args) != 0)
    //     {
    //         printf("avfilter_init_str(aformat) failed.\n");
    //         return -1;
    //     }
    //     printf("用AVFilterLink把相邻的两个滤波实例连接起来");
    //     // abuffer->aformat->sink
    //     if (avfilter_link(objAudioInputInfo.filterCtx, 0, m_pAudioOutputInfo->filterCtx, 0) != 0)
    //     {
    //         printf("avfilter_link(amix, aformat) failed.\n");
    //         return -1;
    //     }
    //     if (avfilter_link(m_pAudioOutputInfo->filterCtx, 0, m_pAudioSinkInfo->filterCtx, 0) != 0)
    //     {
    //         printf("avfilter_link(aformat, abuffersink) failed.\n");

    //         return -1;
    //     }
    // }
    // else if (m_strCustomFilterDescr.empty())
    // {
    //     // 没有自定义滤镜链和aformat，直接 abuffer->sink
    //     if (avfilter_link(objAudioInputInfo.filterCtx, 0, m_pAudioSinkInfo->filterCtx, 0) != 0)
    //         return -1;
    // }
    // // 有自定义滤镜链时，parse_ptr 已自动连接，无需再手动 link

    // printf("提交整个滤波图");
    // if (avfilter_graph_config(m_objFilterGraph, NULL) < 0)
    // {
    //     printf("error");
    //     return -1;
    // }

    // m_initialized = true;

    return 0;
}

int AudioFilter::exit()
{
    std::lock_guard<std::mutex> locker(m_mutex);

    if (m_initialized)
    {
        for (auto &AudioInputInfoIter : m_mapAudioInputInfo)
        {
            if (AudioInputInfoIter.second.filterCtx)
            {
                avfilter_free(AudioInputInfoIter.second.filterCtx);
                AudioInputInfoIter.second.filterCtx = nullptr;
            }
        }
        m_mapAudioInputInfo.clear();

        if (m_pAudioOutputInfo && m_pAudioOutputInfo->filterCtx)
        {
            avfilter_free(m_pAudioOutputInfo->filterCtx);
            m_pAudioOutputInfo->filterCtx = nullptr;
        }
        if (m_pAudioSinkInfo && m_pAudioSinkInfo->filterCtx)
        {
            avfilter_free(m_pAudioSinkInfo->filterCtx);
            m_pAudioSinkInfo->filterCtx = nullptr;
        }
        avfilter_graph_free(&m_objFilterGraph);
        m_objFilterGraph = nullptr;
        m_initialized = false;
    }
    return 0;
}

int AudioFilter::addFrame(uint32_t index, uint8_t *inBuf, uint32_t size)
{
    std::lock_guard<std::mutex> locker(m_mutex);

    if (!m_initialized)
        return -1;

    auto iter = m_mapAudioInputInfo.find(index);
    if (iter == m_mapAudioInputInfo.end())
        return -1;

    if (inBuf && size > 0)
    {
        std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr)
                                         { av_frame_free(&ptr); });
        avFrame->sample_rate = iter->second.samplerate;
        avFrame->format = iter->second.format;
        avFrame->channel_layout = av_get_default_channel_layout(iter->second.channels);
        avFrame->nb_samples = size * 8 / iter->second.samplingAccuracy / iter->second.channels;
        av_frame_get_buffer(avFrame.get(), 1);
        memcpy(avFrame->extended_data[0], inBuf, size);

        if (av_buffersrc_add_frame(iter->second.filterCtx, avFrame.get()) != 0)
            return -1;
    }
    else
    {
        if (av_buffersrc_add_frame(iter->second.filterCtx, NULL) != 0)
            return -1;
    }
    return 0;
}

int AudioFilter::getFrame(uint8_t *outBuf, uint32_t maxOutBufSize)
{
    std::lock_guard<std::mutex> locker(m_mutex);

    if (!m_initialized)
        return -1;

    std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr)
                                     { av_frame_free(&ptr); });

    int ret = av_buffersink_get_frame(m_pAudioSinkInfo->filterCtx, avFrame.get());
    if (ret < 0)
        return -1;

    int size = av_samples_get_buffer_size(NULL, avFrame->channels, avFrame->nb_samples, (AVSampleFormat)avFrame->format, 1);
    if (size > (int)maxOutBufSize)
        return 0;

    memcpy(outBuf, avFrame->extended_data[0], size);
    return size;
}

void AudioFilter::setCustomFilter(const std::string &p_strCustomFilterDescr)
{
    m_strCustomFilterDescr = p_strCustomFilterDescr;
    // 若需运行时切换滤镜链，可在此处调用 exit() + init() 实现重建
}

const std::string &AudioFilter::getCustomFilter() const
{
    return m_strCustomFilterDescr;
}